package net.jdm.domain.entity;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import net.jdm.domain.caculator.DistanceCaculator;
import net.jdm.domain.caculator.PackageCycleCaculator;

public class Package implements ContainerNode{
	private Component component;
    private String name;
    private    boolean isStable;
    private final Set<Class> classes=new HashSet<Class>();
    private final Set<Package> afferents=new HashSet<Package>();
    private final Set<Package> efferents=new HashSet<Package>();
    public Component getComponent() {
		return component;
	}
	public void setComponent(Component component) {
		this.component = component;
	}
	public Package(String name) {
		this(name,false);
	}
    public Package(String name, boolean isStable) {
    	this.name=name;
    	this.isStable=isStable;
	}
    public void addClass(Class clazz) {
    	this.classes.add(clazz);
    }

	public Set<Class> getClasses() {
		return this.classes;
	}
    public int afferentCoupling() {
        return getAfferents().size();
    }

    public int efferentCoupling() {
        return getEfferents().size();
    }
	@Override
	public int afferentCouplingWeight() {
		return getWeight(getAfferents());
	}
	@Override
	public int efferentCouplingWeight() {
		return getWeight(getEfferents());
	}
	@Override
    public float instability() {
		return DistanceCaculator.getInstance().instability(this);
    }
	private int getWeight(Collection<Package> javaPackages) {
		int weight=0;
		for(Package afferent:javaPackages){
			weight+=(afferent.isStable()?0:1);
		}
		return weight;
	}
	@Override
	public Set<Package> getEfferents() {
		return this.efferents;
	}
	@Override
	public Set<Package> getAfferents() {
		return this.afferents;
	}
	public void addAfferent(Package imported) {
		getAfferents().add(imported);
	}
	public void addEfferent(Package exported) {
		getEfferents().add(exported);
	}
    public void dependUpon(Package dependPackage) {
        addEfferent(dependPackage);
        dependPackage.addAfferent(this);
    }
	@Override
	public String getName() {
		return this.name;
	}
	@Override
	public int getClassCount() {
		return this.classes.size();
	}
	@Override
    public int getAbstractClassCount() {
        int count = 0;

        for (Iterator<Class> i = classes.iterator(); i.hasNext();) {
            Class clazz = i.next();
            if (clazz.isAbstract()) {
                count++;
            }
        }
        return count;
    }

    public int getConcreteClassCount() {
        int count = 0;

        for (Iterator<Class> i = classes.iterator(); i.hasNext();) {
            Class clazz = i.next();
            if (!clazz.isAbstract()) {
                count++;
            }
        }

        return count;
    }
	@Override
	public float abstractness() {
		return DistanceCaculator.getInstance().abstractness(this);
	}
	@Override
	public float distance() {
		return DistanceCaculator.getInstance().distance(this);
	}
	public Collection<List<Package>> getCycles(){
		List<List<Package>> result=new ArrayList<List<Package>>();
		for(List<ContainerNode> crycle:PackageCycleCaculator.getInstance().getCycles(this)){
			List<Package> packageCrycle=new LinkedList<Package>();
			for(ContainerNode node:crycle){
				packageCrycle.add((Package)node);
			}
			result.add(packageCrycle);
		}
		return result;
	}
	@Override
	public boolean isStable() {
		return isStable;
	}
	public void setStable(boolean isStable) {
		this.isStable=isStable;
	}
	@Override
	public boolean equals(Object other){
        if (other!=null&&other.getClass().equals(this.getClass())) {
        	Package otherPackage = (Package) other;
            return otherPackage.getName().equals(getName());
        }
        return false;
	}
	@Override
    public int hashCode() {
        return toString().hashCode();
    }
	@Override
    public String toString(){
    	return getName();
    }
}
